#include "lock_utils.h"
#include <stdexcept>
#include <sys/time.h>
#include <errno.h>
#include <string.h>

#define SITE_CFG_RW_TIMEOUT 0
// #define SITE_CFG_RW_TIMEOUT (15 * 1000)

static_assert(SITE_CFG_RW_TIMEOUT >= 0, "rw timeout can not be neg");
using namespace std;

namespace servicesite {

    void pthread_rwlock_guard::lock()
    {
        if (_locked)
            return;
#if SITE_RWLOCK_TRACE
        LOG_INFO("Try lock {:016X}, {}", (intptr_t)this, _write_lock);
#endif
        int res;
        //NOTE: 如果返回非0值是什么情况
#if SITE_CFG_RW_TIMEOUT == 0
        res = _write_lock ? pthread_rwlock_wrlock(_mutex) : pthread_rwlock_rdlock(_mutex);
#else
        //还有个sem_clockwait用的是开机时间, 可以避免开始等待后, 系统时间修改的影响, 但那个接口太新了没法用(POSIX.1-2024)
        struct timespec ts{};
        struct timeval tt{};

        // int status = clock_gettime(CLOCK_REALTIME, ts);
        gettimeofday(&tt, nullptr);

        //不会溢出的上限是2147.483647ms-1000=1147ms, 还是需要处理下的
        ts.tv_sec = tt.tv_sec + SITE_CFG_RW_TIMEOUT / 1000;
#if (SITE_CFG_RW_TIMEOUT % 1000) != 0
        ts.tv_nsec = tt.tv_usec * 1000 + (SITE_CFG_RW_TIMEOUT % 1000) * 1000 * 1000;
        ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000);
        ts.tv_nsec %= (1000 * 1000 * 1000);
#endif
        res = _write_lock ? pthread_rwlock_timedwrlock(_mutex, &ts) : pthread_rwlock_timedrdlock(_mutex, &ts);
#endif
        if (res != 0) {
            string msg = (string)"lock failed(" + to_string(res) + "): " + strerror(res);
            throw lock_exception(this, msg);
        }
        _locked = true;
#if SITE_RWLOCK_TRACE
        LOG_INFO("Locked {:016X}, {}", (intptr_t)this, _write_lock);
#endif
    }

    void pthread_rwlock_guard::unlock(bool no_throw)
    {
        if (!_locked)
            return;
#if SITE_RWLOCK_TRACE
        LOG_INFO("Unlock {:016X}, {}", (intptr_t)this, _write_lock);
#endif
        /*
         * 如果实现检测到 pthread_rwlock_unlock() 的 rwlock 参数指定的值未引用已初始化的读写锁对象，则建议该函数失败并报告 [EINVAL] 错误。
         * 如果实现检测到 pthread_rwlock_unlock() 的 rwlock 参数指定的值引用当前线程未持有锁的读写锁对象，则建议该函数失败并报告 [EPERM] 错误。
         * 不抛出以免析构函数出错
         */
        int res = pthread_rwlock_unlock(_mutex);
        if (res == 0) {
            _locked = false;
        }
        else if (!no_throw) {
            string msg = (string)"unlock failed(" + to_string(res) + "): " + strerror(res);
            throw lock_exception(this, msg);
        }
    }

    void pthread_rwlock_recursive_guard::lock()
    {
        if (_locked)
            return;
        auto tid = pthread_self();
        if (_data->tid != tid)
        {
            string msg = (string)"lock from a different thread, create tid: " + to_string(_data->tid) + ", unlock tid: " + to_string(tid);
            throw lock_exception(this, msg);
        }
        if (_write_lock && _data->writer_cnt == 0 && _data->reader_cnt > 0)
        {
            string msg = (string)"Can't aquire a writer lock while current thread already hold a reader lock: " + to_string(_data->reader_cnt);
            throw lock_exception(this, msg);
        }
#if SITE_RWLOCK_TRACE
        LOG_INFO("Try lock recursive {:016X}, {}, r: {}, w: {}", (intptr_t)this, _write_lock, _data->writer_cnt, _data->reader_cnt);
#endif
        int& lock_cnt = _write_lock ? _data->writer_cnt : _data->reader_cnt;
        if (_write_lock) {
            if (_data->writer_cnt + 1 < 0) {
                string msg = (string)"writer_cnt overflow: " + to_string(_data->writer_cnt);
                throw lock_exception(this, msg);
            }
        }
        else {
            if (_data->reader_cnt + 1 < 0) {
                string msg = (string)"reader_cnt overflow: " + to_string(_data->reader_cnt);
                throw lock_exception(this, msg);
            }
        }
        bool aquire_lock = (_data->writer_cnt == 0 && _data->reader_cnt == 0);
        if (aquire_lock) {
            int res;
            //NOTE: 如果返回非0值是什么情况
#if SITE_CFG_RW_TIMEOUT == 0
            res = _write_lock ? pthread_rwlock_wrlock(_mutex) : pthread_rwlock_rdlock(_mutex);
#else
            //还有个sem_clockwait用的是开机时间, 可以避免开始等待后, 系统时间修改的影响, 但那个接口太新了没法用(POSIX.1-2024)
            struct timespec ts{};
            struct timeval tt{};

            // int status = clock_gettime(CLOCK_REALTIME, ts);
            gettimeofday(&tt, nullptr);

            //不会溢出的上限是2147.483647ms-1000=1147ms, 还是需要处理下的
            ts.tv_sec = tt.tv_sec + SITE_CFG_RW_TIMEOUT / 1000;
#if (SITE_CFG_RW_TIMEOUT % 1000) != 0
            ts.tv_nsec = tt.tv_usec * 1000 + (SITE_CFG_RW_TIMEOUT % 1000) * 1000 * 1000;
            ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000);
            ts.tv_nsec %= (1000 * 1000 * 1000);
#endif
            res = _write_lock ? pthread_rwlock_timedwrlock(_mutex, &ts) : pthread_rwlock_timedrdlock(_mutex, &ts);
#endif
            if (res != 0) {
                string msg = (string)"lock failed(" + to_string(res) + "): " + strerror(res);
                throw lock_exception(this, msg);
            }
        }
        _locked = true;
        if (_write_lock)
            _data->writer_cnt++;
        else
            _data->reader_cnt++;
#if SITE_RWLOCK_TRACE
        LOG_INFO("Locked recursive {:016X}, {}, r: {}, w: {}", (intptr_t)this, _write_lock, _data->writer_cnt, _data->reader_cnt);
#endif
    }

    void pthread_rwlock_recursive_guard::unlock(bool no_throw)
    {
        if (!_locked)
            return;
        auto tid = pthread_self();
        if (_data->tid != tid)
        {
            if (no_throw)
                return;
            string msg = (string)"unlock from a different thread, create tid: " + to_string(_data->tid) + ", unlock tid: " + to_string(tid);
            throw lock_exception(this, msg);
        }
#if SITE_RWLOCK_TRACE
        LOG_INFO("Unlock recursive {:016X}, {}, r: {}, w: {}", (intptr_t)this, _write_lock, _data->writer_cnt, _data->reader_cnt);
#endif
        //TODO: 确保解锁时顺序和加锁的对应
        if (_write_lock)
        {
            if (_data->writer_cnt <= 0) {
                if (no_throw)
                    return;
                string msg = (string)"writer_cnt overflow: " + to_string(_data->writer_cnt);
                throw lock_exception(this, msg);
            }
            _data->writer_cnt--;
        }
        else
        {
            if (_data->reader_cnt <= 0) {
                if (no_throw)
                    return;
                string msg = (string)"reader_cnt overflow: " + to_string(_data->writer_cnt);
                throw lock_exception(this, msg);
            }
            _data->reader_cnt--;
        }
        if (_data->writer_cnt == 0 && _data->reader_cnt == 0)
        {
            /*
            * 如果实现检测到 pthread_rwlock_unlock() 的 rwlock 参数指定的值未引用已初始化的读写锁对象，则建议该函数失败并报告 [EINVAL] 错误。
            * 如果实现检测到 pthread_rwlock_unlock() 的 rwlock 参数指定的值引用当前线程未持有锁的读写锁对象，则建议该函数失败并报告 [EPERM] 错误。
            * 不抛出以免析构函数出错
            */
            int res = pthread_rwlock_unlock(_mutex);
            if (res != 0 && !no_throw) {
                string msg = (string)"unlock failed(" + to_string(res) + "): " + strerror(res);
                throw lock_exception(this, msg);
            }
        }
        _locked = false;
    }
}